/********************************
 * Robinson Matrix Algorithm
 *
 * Copyright (C) 2009 CNRS
 * Author : Florent AVELLANEDA, Eugen CEPOI
 *
 *
 * All rights reserved.
 *
 *   This file is part of Robinson Matrix Algorithm.
 *
 *   Robinson Matrix Algorithm is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   Robinson Matrix Algorithm is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with Robinson Matrix Algorithm.  If not, see <http://www.gnu.org/licenses/>.
 *
 ***********************************************************************************/
#include "Matrix.h"

#include <iostream>
#include <cassert>
#include <sstream>
#include <cmath>
#include <time.h>
#include <cstdlib>

#include "Algo.h"
#include "Util.h"

using namespace Util;
using namespace std;

    Matrix::Matrix()
: _dMaxDist(0)
{

}

Matrix::Matrix ( vector<vector<double> > m )
{
    _vvMatrix = m;
}

    Matrix::Matrix( Matrix *m, std::vector<unsigned int> col )
: _dMaxDist(0), _colsInMatrix(col), _mainMatrix(m)
{
    unsigned int l;

    _vvMatrix.resize(_colsInMatrix.size());

    for ( unsigned int i = 0; i < _colsInMatrix.size(); ++i )
        _vvMatrix[i].resize(_colsInMatrix.size());

    for ( unsigned int i = 0; i < _colsInMatrix.size(); ++i )
    {
        l = 0;

        for ( unsigned int j = 0; j < _colsInMatrix.size(); ++j)
        {
            set(i, l, m->get(_colsInMatrix[i], _colsInMatrix[j]));
            ++l;
        }
    }
}

Matrix::~Matrix()
{
}

void Matrix::set(unsigned int x, unsigned int y, double d)
{
    assert( (x < _vvMatrix.size()) || !"erreur" );
    assert( (y < _vvMatrix.size()) || !"erreur" );
    assert( (x < _vvMatrix[y].size()) || !"erreur" );
    assert( (y < _vvMatrix[x].size()) || !"erreur" );

    if( d > _dMaxDist )
        _dMaxDist = d;

    _vvMatrix[x][y] = d;
    _vvMatrix[y][x] = d;
}

void Matrix::setSize(unsigned int size)
{
    _vvMatrix.resize(size);
    for ( unsigned int i = 0; i < _vvMatrix.size(); ++i )
        _vvMatrix[i].resize(size);
}

void Matrix::setFictifBounds()
{
    _vvMatrix.resize(_vvMatrix.size()+2);
    for ( unsigned int i = 0; i < _vvMatrix.size(); ++i )
        _vvMatrix[i].resize(_vvMatrix.size(),0);

    double maxDist = _dMaxDist;
    unsigned int size = _vvMatrix.size()-1;
    for ( unsigned int i = 0; i <= (size-1); ++i )
    {
        set(i, size-1, 5*maxDist);
        set(i, size, 5*maxDist);
    }

    set(size-1, size, 10*maxDist);
    _dMaxDist = 10*maxDist;
}

void Matrix::setElementFromMainMatrix( unsigned int element )
{
    _vvMatrix.resize(_vvMatrix.size()+1);
    for ( unsigned int i = 0; i < _vvMatrix.size(); ++i )
        _vvMatrix[i].resize(_vvMatrix.size(),0);

    int size = _vvMatrix.size()-1;
    for ( unsigned int i = 0;  i < (_vvMatrix.size()-1); ++i )
        set(i, size, _mainMatrix->get(_colsInMatrix[i], element));

    _colsInMatrix.push_back(element);
}

void Matrix::removeFictifBounds()
{
    for ( unsigned int i = 0; i < _vvMatrix.size(); ++i )
        _vvMatrix[i].erase(_vvMatrix[i].begin()+_vvMatrix[i].size()-2, _vvMatrix[i].end());

    _vvMatrix.erase(_vvMatrix.begin()+_vvMatrix.size()-2, _vvMatrix.end());
}

std::vector<unsigned int> Matrix::getColsInMatrix()
{
    return _colsInMatrix;
}


void Matrix::randomGen3(unsigned int size, double maxDelta)
{
    _vvMatrix.resize(size);
    for (unsigned int i = 0; i < _vvMatrix.size(); i++)
        _vvMatrix[i].resize(size);


    for (unsigned int i = 1; i < size; i++)
        for (unsigned int j = 0; j < (size-i); j++)
            set(i+j, j, abs((int)((rand() % (int)(5+maxDelta*2)) + max( get(i+j-1, j), get(i+j, j+1) ) - maxDelta)) );

    std::vector<unsigned int> totalOrder;
    for (unsigned int i = 0; i < size; i++)
        totalOrder.push_back(i);

            Algo::TotalOrder2RobinsonMatrix(*this, totalOrder, maxDelta);
}

void Matrix::randomGen2(unsigned int size, int maxVal)
{
    _vvMatrix.resize(size);
    for (unsigned int i = 0; i < _vvMatrix.size(); i++)
        _vvMatrix[i].resize(size);

    for (unsigned int i = 0; i < size; i++)
        for (unsigned int j = i+1; j < size; j++)
            set(i, j, rand() % maxVal);
}

void Matrix::randomGen(unsigned int size, int maxVal)
{
    std::pair<unsigned int, unsigned int> points[size];
    for (unsigned int i = 0; i < size; i++)
    {
        points[i].first =  (rand() % maxVal);
        points[i].second = (rand() % maxVal);
    }

    _vvMatrix.resize(size);
    for (unsigned int i = 0; i < _vvMatrix.size(); i++)
        _vvMatrix[i].resize(size);


    for (unsigned int i = 0; i < size; i++)
        for (unsigned int j = i+1; j < size; j++)
            set(i, j, (int)sqrt(
                        (points[i].first - points[j].first)*(points[i].first - points[j].first) +
                        (points[i].second - points[j].second)*(points[i].second - points[j].second)));
}

void Matrix::randomRobinsonMatrix(unsigned int size)
{
    srand(time(0));
    _vvMatrix.resize(size);
    for (unsigned int i = 0; i < _vvMatrix.size(); i++)
        _vvMatrix[i].resize(size, 0);

    set(0, size-1, rand()%100);
    for ( unsigned int i = 1; i < (size-1); ++i )
    {
        set(i, size-1, rand()%(int)(_vvMatrix[i-1][size-1]-1) + 1);
        set(0, size-i-1, rand()%(int)(_vvMatrix[0][size-i]-1) +1 );
    }

	set(0,0,0);

	for (unsigned int i = 1; i < (size-2); ++i )
		for (unsigned int j = (size-2); j > i; --j )
			set(i,j, rand()%(int)(min(_vvMatrix[i-1][j],_vvMatrix[i][j+1])-1) + 1);
}

void Matrix::clean()
{
	_vvMatrix.clear();
	_colsInMatrix.clear();
}

std::ostream& operator << (std::ostream& os, const Matrix& m)
{
    os << m.getSize() << endl;
    for (unsigned int i = 0; i < m.getSize(); i++)
    {
        for (unsigned int j = 0; j < m.getSize(); j++)
            os << m.get(i,j) << "\t";
        os << endl;
    }
    return os;
}

std::istream& operator >> (std::istream& is, Matrix& m)
{
    unsigned int size;
    is >> size;

    m._vvMatrix.resize(size);
    for (unsigned int i = 0; i < m._vvMatrix.size(); i++)
        m._vvMatrix[i].resize(size);

    for (unsigned int i = 0; i < m._vvMatrix.size(); i++)
        for (unsigned int j = 0; j < size; j++)
        {
            double d;
            is >> d;
            m.set(i, j, d);
        }

    return is;
}



